package com.neusoft.neuiotms.dcvm.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.collections4.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.google.common.base.Joiner;
import com.neusoft.bizcore.cache.caching.CacheService;
import com.neusoft.neuiotms.dcvm.bean.AttrBean;
import com.neusoft.neuiotms.dcvm.bean.LocationCiBean;
import com.neusoft.neuiotms.dcvm.bean.StandardCiBean;
import com.neusoft.neuiotms.dcvm.service.AssetService;
import com.neusoft.neuiotms.dcvm.service.CapacityService;
import com.neusoft.neuiotms.dcvm.service.LocationService;
import com.neusoft.neuiotms.dcvm.utils.Constants;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class CapacityServiceImpl implements CapacityService {

    @Autowired
    private LocationService locationService;
    @Autowired
    private AssetService assetService;
    @Autowired
    private CacheService cacheService;

    @Value("#{'${dcvm.capacity.cabinet.locations}'.split(',')}")
    private List<String> locations;

    //每天凌晨0点执行
    @Scheduled(cron = "0 0 0 * * ?")
    public void run() {
        this.locations.forEach(loc -> {
            try {
                final List<Map<String, Object>> data = this.scheduledCabinetCapacity(loc);
                this.cacheService.put(Constants.CAPACITY_CACHE_NAME, loc, data);
            } catch (final Exception e) {
                CapacityServiceImpl.log.error(e.getMessage(), e);
            }

        });
    }

    @Override
    public List<Map<String, Object>> scheduledCabinetCapacity(final String location) {
        // 根据location查询所有的子位置
        final List<LocationCiBean> locations = this.locationService.getDeepLocationsByOne(location, Integer.MAX_VALUE);
        final String foreignValue =
                Joiner.on(",").join(locations.stream().map(it -> it.getCode()).collect(Collectors.toList()));
        // 查询指定位置下的所有机柜
        final List<StandardCiBean> allCabinets =
                this.assetService.getAllCiByCatetoriesAndForeign("CT_RACK", "location", foreignValue);

        final List<Map<String, Object>> infos = new ArrayList<>();
        for (final StandardCiBean cabinet : allCabinets) {

            final Map<String, Object> space = new HashedMap<>();
            final Optional<AttrBean> spaceTotalAttr =
                    cabinet.getAttrs().stream().filter(it -> "uHeight".equals(it.getKey())).findAny();
            if (spaceTotalAttr.isPresent()) {
                space.put("unit", "U");
                int total = 0;
                try {
                    total = Integer.parseInt(spaceTotalAttr.get().getValue().toString());
                } catch (final Exception e) {
                }
                space.put("total", total);
            }

            final List<StandardCiBean> assetsInCabinet =
                    this.assetService.getAllCiByForeignInst("cabinet", cabinet.getCode());
            int uHeightSum = 0;
            double ratedPowerSum = 0;
            double weightSum = 0;
            long uInfo = 0;

            for (final StandardCiBean asset : assetsInCabinet) {
                final Optional<AttrBean> opt =
                        asset.getAttrs().stream().filter(it -> "uHeight".equals(it.getKey())).findAny();
                if (opt.isPresent()) {
                    int val = 0;
                    try {
                        val = Integer.parseInt(opt.get().getValue().toString());
                    } catch (final Exception e) {
                    }
                    uHeightSum += val;

                    final Optional<AttrBean> optUbit =
                            asset.getAttrs().stream().filter(it -> "ubit".equals(it.getKey())).findAny();
                    if (optUbit.isPresent()) {
                        int ubit = 0;
                        try {
                            ubit = Integer.parseInt(optUbit.get().getValue().toString());
                        } catch (final Exception e) {
                        }
                        long assetUInfo = 0;
                        if (ubit > 0) {
                            for (int i = 0; i < val; i++) {
                                assetUInfo = (assetUInfo << 1) + 1;
                            }
                            assetUInfo = assetUInfo << (ubit - 1);
                        }
                        uInfo = uInfo | assetUInfo;
                    }
                }

                final Optional<AttrBean> opt1 =
                        asset.getAttrs().stream().filter(it -> "ratedPower".equals(it.getKey())).findAny();
                if (opt1.isPresent()) {
                    double val = 0;
                    try {
                        val = Double.parseDouble(opt1.get().getValue().toString());
                    } catch (final Exception e) {
                    }
                    ratedPowerSum += val;
                }

                final Optional<AttrBean> opt2 =
                        asset.getAttrs().stream().filter(it -> "weight".equals(it.getKey())).findAny();
                if (opt2.isPresent()) {
                    double val = 0;
                    try {
                        val = Double.parseDouble(opt2.get().getValue().toString());
                    } catch (final Exception e) {
                    }
                    weightSum += val;
                }
            }

            // 机柜空间容量
            if (spaceTotalAttr.isPresent()) {
                final int total = (Integer) space.get("total");
                if (total > 0) {
                    space.put("percent",
                            new BigDecimal((uHeightSum * 1.0) / total).setScale(4, BigDecimal.ROUND_HALF_DOWN)
                                    .doubleValue());
                } else {
                    space.put("percent", 0);
                }
                space.put("used", uHeightSum);
                String uInfoStr = Long.toString(uInfo, 2);
                if (uInfoStr.length() < total) {
                    final StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < (total - uInfoStr.length()); i++) {
                        sb.append("0");
                    }
                    sb.append(uInfoStr);
                    uInfoStr = sb.toString();
                }
                space.put("uInfo", uInfoStr);
            }

            // 机柜电力容量
            final Map<String, Object> electric = new HashedMap<>();
            final Optional<AttrBean> electricTotalAttr =
                    cabinet.getAttrs().stream().filter(it -> "ratedPower".equals(it.getKey())).findAny();
            if (electricTotalAttr.isPresent()) {
                electric.put("unit", "W");
                double total = 0;
                try {
                    total = Double.parseDouble(electricTotalAttr.get().getValue().toString());
                } catch (final Exception e) {
                }

                if (total > 0) {
                    electric.put("percent",
                            new BigDecimal((ratedPowerSum * 1.0) / total).setScale(4, BigDecimal.ROUND_HALF_DOWN)
                                    .doubleValue());
                } else {
                    electric.put("percent", 0);
                }
                electric.put("used",
                        new BigDecimal(ratedPowerSum).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue());
                electric.put("total", new BigDecimal(total).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue());
            }

            // 机柜承重容量
            final Map<String, Object> weight = new HashedMap<>();
            final Optional<AttrBean> weightTotalAttr =
                    cabinet.getAttrs().stream().filter(it -> "maxWeight".equals(it.getKey())).findAny();
            if (weightTotalAttr.isPresent()) {
                weight.put("unit", "KG");
                double total = 0;
                try {
                    total = Double.parseDouble(weightTotalAttr.get().getValue().toString());
                } catch (final Exception e) {
                }
                if (total > 0) {
                    weight.put("percent",
                            new BigDecimal((weightSum * 1.0) / total).setScale(4, BigDecimal.ROUND_HALF_DOWN)
                                    .doubleValue());
                } else {
                    weight.put("percent", 0);
                }
                weight.put("used", new BigDecimal(weightSum).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue());
                weight.put("total", new BigDecimal(total).setScale(2, BigDecimal.ROUND_HALF_DOWN).doubleValue());
            }

            final Map<String, Object> capaInfo = new HashedMap<>();
            capaInfo.put("electric", electric);
            capaInfo.put("weight", weight);
            capaInfo.put("space", space);

            final Map<String, Object> info = new HashedMap<>();
            info.put("cabinetCode", cabinet.getCode());
            info.put("capaInfo", capaInfo);

            infos.add(info);
        }
        return infos;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Map<String, Object>> getCabinetCapacity(final String location) {
        final boolean cached = this.cacheService.isInCache(Constants.CAPACITY_CACHE_NAME, location);
        if (!cached) {
            final List<Map<String, Object>> data = this.scheduledCabinetCapacity(location);
            this.cacheService.put(Constants.CAPACITY_CACHE_NAME, location, data);
            return data;
        } else {
            final Object data = this.cacheService.get(Constants.CAPACITY_CACHE_NAME, location);
            return (List<Map<String, Object>>) data;
        }
    }

}
